﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using System.Xml;
using PloobsEngine.Cameras;
using PloobsEngine.Light;
using PloobsEngine.Entity;
using PloobsEngine.Physics;
using PloobsEngine.Utils;
using PloobsEngine.Particle3D;
using PloobsEngine.Trigger;
using PloobsEngine.Audio;
using Microsoft.Xna.Framework;
using PloobsEngine.Modelo;

namespace PloobsEngine.SceneControl
{
    public class BrutalForceWorld : IWorld
    {
        private List<ISoundEmitter3D> _emiter = new List<ISoundEmitter3D>();       
        private IDictionary<string, object> _namesToEntities = new Dictionary<string, object>();
        private IPhysicWorld _physicWorld = null;
        private IList<IObject> _worldObjects = new List<IObject>();
        private IList<ITrigger> _triggers = new List<ITrigger>();
        private IList<IDummy> _dummies = new List<IDummy>();
        private EntityMapper _entityMapper = EntityMapper.getInstance();
        private CameraManager _cameraManager = new CameraManager();
        private IList<ILight> _lights = new List<ILight>();
        private ICuller _culler;
        private ParticleManager _particleManager;

        /// <summary>
        /// Serialization Usage
        /// </summary>
        public BrutalForceWorld()
        {
            Init();
        }

        public BrutalForceWorld(IPhysicWorld physicWorld)
        {
            this._physicWorld = physicWorld;
            Init();
        }

        private void Init()
        {
            _culler = new DummyCuller();
            _particleManager = new ParticleManager();
        }

        /// <summary>
        /// Adiciona um Sound Emitter
        /// </summary>
        /// <param name="em"></param>
        public void AddSoundEmitter(ISoundEmitter3D em)
        {
            em.World = this;
            EntityMapper.getInstance().AddEntity(em);
            //em.setEmitterPosition(Vector3.Zero);
            em.setListenerPosition(new Vector3(1000, 0, 0));
            _emiter.Add(em);
            //em.Play();

        }

        public void RemoveSoundEmitter(int id)
        {
            IEntity e = EntityMapper.getInstance().getEntity(id);
            EntityMapper.getInstance().RemoveEntity(e);
            (e as ISoundEmitter3D).Stop();
            _emiter.Remove(e as ISoundEmitter3D);
        }

        public void RemoveSoundEmitter(ISoundEmitter3D e)
        {
            e.Stop();
            EntityMapper.getInstance().RemoveEntity(e);
            _emiter.Remove(e);

        }


        #region IWorld Members


        public List<CameraDescription> cameraList()
        {
            return _cameraManager.GetCamerasDescription();
        }       

        public int AddObject(IObject obj)
        {
            int id =_entityMapper.AddEntity(obj);
            _worldObjects.Add(obj);
            obj.PhysicObject.ObjectOwner = obj;
            _physicWorld.AddObject(obj.PhysicObject);
            if (!String.IsNullOrEmpty(obj.Name))
                _namesToEntities.Add(obj.Name, obj);

            _culler.onObjectAdded(obj);
            return id;

        }

        public void RemoveObject(IObject obj)
        {
            _worldObjects.Remove(obj);
            obj.ToBeRemoved = true;
            _physicWorld.RemoveObject(obj.PhysicObject);
            _entityMapper.RemoveEntity(obj);
            if (obj.Behavior != null)
                obj.Behavior.ReleaseBehaviors();

            if (!String.IsNullOrEmpty(obj.Name))
                _namesToEntities.Remove(obj.Name);            

            _culler.onObjectRemoved(obj);            
        }

        public void RemoveObject(int id)
        {
            IEntity entity = EntityMapper.getInstance().getEntity(id);
            if (entity is IObject)
            {
                IObject obj = entity as IObject;
                if (!String.IsNullOrEmpty(obj.Name))
                    _namesToEntities.Remove(obj.Name);

                obj.ToBeRemoved = true;
                _worldObjects.Remove(obj);
                _culler.onObjectRemoved(obj);
                _entityMapper.RemoveEntity(id);
            }
        }

        public void UpdateWorld(Microsoft.Xna.Framework.GameTime gt)
        {
            PhysicWorld.Update(gt);

            if (_cameraManager.ActiveCamera != null) 
            _cameraManager.ActiveCamera.Update(gt);

            List<IObject> remove = new List<IObject>();

            foreach (IObject objcts in _worldObjects)
            {
                objcts.CameraDistance = Math.Abs((objcts.Position - _cameraManager.ActiveCamera.Position).Length());
                objcts.UpdateObject(gt,_cameraManager.ActiveCamera,_lights);                
                if (objcts.ToBeRemoved == true)
                {
                    remove.Add(objcts);
                }
            }

            foreach (IObject item in remove)
            {
                this.RemoveObject(item);
            }

            _particleManager.Update(gt);


            foreach (ISoundEmitter3D item in _emiter)
            {
                item.Update(gt);
                item.setListenerPosition(_cameraManager.ActiveCamera.Position);
            }


        }

        public void DrawnWorld(Microsoft.Xna.Framework.GameTime gt,IRenderHelper render)
        {
            // no deferred, nao eh desenhado aq
            //if (PhysicWorld.isDebugDraw)
            //{
            //    PhysicWorld.DebugDrawn(gt, _cameraManager.ActiveCamera);
            //}

            //foreach (IObject objcts in _worldObjects)
            //{
            //    objcts.PreDrawn(gt,  _cameraManager.ActiveCamera, _lights, render);
            //}

            foreach (IObject objcts in _worldObjects)
            {
                objcts.Drawn(gt,_cameraManager.ActiveCamera,_lights,render);
            }
        }

        public bool Contain(IObject obj)
        {
            if (obj == null)
                return false;
            return _worldObjects.Contains(obj);
        }

        public int AddLight(ILight light)
        {
            int id = 0;
            if (light is IEntity)
            {
                id = _entityMapper.AddEntity(light as IEntity);
            }
            _lights.Add(light);

            if (!String.IsNullOrEmpty(light.Name))
            {
                _namesToEntities.Add(light.Name, light);
            }
            return id;
        }

        public void RemoveLight(ILight light)
        {
            if (light is IEntity)
            {
                _entityMapper.RemoveEntity(light as IEntity);
            }
            if (!String.IsNullOrEmpty(light.Name))
            {
                _namesToEntities.Remove(light.Name);
            }
            _lights.Remove(light);
        }

        public ICamera ActiveCamera
        {
            get
            {
                return _cameraManager.ActiveCamera;
            }
            
        }

        #endregion

        #region IWorld Members


        /// <summary>
        /// APENAS ADICINA, NAO ATIVA
        /// </summary>
        /// <param name="nome"></param>
        /// <param name="cam"></param>
        public void AddCamera(string nome, ICamera cam)
        {
            if (!String.IsNullOrEmpty(nome))
            {
                //if(_namesToEntities.ContainsKey(nome) && _namesToEntities[nome] == cam )
                _namesToEntities.Add(nome, cam);
                cam.Name = nome;
                _cameraManager.AddCamera(cam, nome);
            }
            else
            {
                throw new Exception("Nome invalido");
            }
            
        }

        public void RemoveCamera(string nome)
        {
            if (!String.IsNullOrEmpty(nome))
            {
                this.CameraManager.RemoveCamera(nome);
                _namesToEntities.Remove(nome);                
            }
            else
            {
                throw new Exception("Nome invalido");
            }
        }

        /// <summary>
        /// ATIVA UMA CAMERA
        /// </summary>
        /// <param name="nome"></param>
        public void setActiveCamera(string nome)
        {
            _cameraManager.SetActiveCamera(nome);
        }

        /// <summary>
        /// ADICIONA E ATIVA
        /// </summary>
        /// <param name="cam"></param>
        public void AddCamera(ICamera cam)
        {            
            _cameraManager.AddCamera(cam);
        }
        
         public IPhysicWorld PhysicWorld
        {
            get
            {
                return _physicWorld;
            }
            set
            {
                this._physicWorld = value;
            }
        }

        public void AddTrigger(ITrigger trigger)
        {
            if (!string.IsNullOrEmpty(trigger.Name))
            {
                _namesToEntities.Add(trigger.Name, trigger);
            }
            _triggers.Add(trigger);

            if(trigger.GhostObject!=null)
            _physicWorld.AddObject(trigger.GhostObject);
            
        }

        public void RemoveTrigger(ITrigger trigger)
        {
            if (!string.IsNullOrEmpty(trigger.Name))
            {
                _namesToEntities.Remove(trigger.Name);
            }
            _triggers.Remove(trigger);
            if(trigger.GhostObject!=null)
            _physicWorld.RemoveObject(trigger.GhostObject);
        }

      
        public string ActiveCameraName()
        {
            return _cameraManager.GetActiveCameraName();
        }

     
        public IList<ILight> Lights
        {
            get { return _lights; }
        }

        public IList<IObject> Objects
        {
            get { return _worldObjects; }
        }

        public int ObjectsNum
        {
            get { return _worldObjects.Count; }
        }
       
        public int Polycont
        {
            get 
            {
                int count=0;
                foreach (IObject ob in _worldObjects)
                {

                    int n = ob.Modelo.MeshNumber;
                    for (int i = 0; i < n; i++)
                    {
                        BatchInformation[] bi = ob.Modelo.GetBatchInformation(i);
                        for (int j = 0; j < bi.Count(); j++)
                        {
                            count += bi[j].PrimitiveCount;
                        }
                    }
                }
                return count;
            }
        }



        #endregion




        #region IWorld Members


        public RayTestInfo SegmentIntersect(Microsoft.Xna.Framework.Ray raio,SegmentInterceptMethod method,float maxDistance)
        {
            SegmentInterceptInfo po = PhysicWorld.SegmentIntersect(raio,method,maxDistance);
            if (po != null)
            {                
                    RayTestInfo ri = new RayTestInfo();
                    ri.Distance = po.Distance;
                    ri.ImpactNormal = po.ImpactNormal;
                    ri.ImpactPosition = po.ImpactPosition;
                    if(po.PhysicObject!= null)
                    ri.Object = po.PhysicObject.ObjectOwner;
                    return ri;    
                
            }
            return null;
        }

        #endregion

        #region IWorld Members


        #endregion

        #region IWorld Members


        public CameraManager CameraManager
        {
            get { return _cameraManager; }
        }

        #endregion

        #region IWorld Members


        public void Serialize(System.Xml.XmlTextWriter textWriter)
        {
            SerializerHelper.SerializeNewElement("PhysicWorld", textWriter);
            SerializerHelper.SerializeAttributeBaseType<string>(PhysicWorld.GetType().AssemblyQualifiedName, "Type", textWriter);
            PhysicWorld.Serialize(textWriter);
            SerializerHelper.SerializeEndElement(textWriter);
        }

        public void DeSerialize(System.Xml.XmlNode node, EngineStuff engine)
        {
            XmlElement mod = node["PhysicWorld"];
            string modtype = SerializerHelper.DeserializeAttributeBaseType<string>("Type", mod);
            Type tt = Type.GetType(modtype);
            IPhysicWorld pw = Activator.CreateInstance(tt) as IPhysicWorld;
            pw.DeSerialize(mod, engine);
            Init();
            this.PhysicWorld = pw;

        }

        #endregion

        #region IWorld Members

        public T GetWorldEntityByName<T>(string Name)
        {
            return (T)_namesToEntities[Name];
        }

        public List<T> GetWorldGroupEntityByName<T>(string Name)
        {
            List<T> resp = new List<T>();
            foreach (var item in  _namesToEntities.Where((p1, p2) =>  p1.Key.Split(new String[] { PrincipalConstants.SEPARATOR_CHARACTER},StringSplitOptions.None)[0]==Name ))
            {                
                resp.Add((T)item.Value);
            }
            return  resp;
        }

        #endregion

        #region IWorld Members

        public bool ContainNamedEntity(string name)
        {
             return _namesToEntities.ContainsKey(name);
        }

        #endregion

        #region IWorld Members

        public void ChangeEntityName(string oldName, string newName)
        {
            object resp = _namesToEntities[oldName];
            _namesToEntities.Remove(oldName);
            _namesToEntities.Add(newName, resp);
        }

        #endregion

        #region IWorld Members

        public ICuller Culler
        {
            get
            {
                return this._culler;
            }
            set
            {
                this._culler = value;
                foreach (var item in _worldObjects)
                {
                    this._culler.onObjectAdded(item);    
                }
            }
        }

        #endregion

        #region IWorld Members

        public void AddDummy(IDummy dummy)
        {
            if (!string.IsNullOrEmpty(dummy.Name))
            {
                _namesToEntities.Add(dummy.Name, dummy);
            }
            _dummies.Add(dummy);
        }

        public void RemoveDummy(IDummy dummy)
        {
            if (!string.IsNullOrEmpty(dummy.Name))
            {
                _namesToEntities.Remove(dummy.Name);
            }
            _dummies.Remove(dummy);
        }

        public IList<IDummy> GetDummyes()
        {
            return _dummies;
        }
      
        #endregion


        #region IWorld Members

        public ParticleManager ParticleManager
        {
            get { return _particleManager; }
        }

        #endregion       
        
    }
}
